//
// Created by dqchen on 2023/1/4.
//
#include <iostream>
#include "intermediate.h"
#include "symbol.h"
#define MARK(x) x>=0 ? "+" : ""

Order::Order() {
}
Order::Order(string lab) {
    label = lab;
};

Order::Order(Operation o, Var *l, Var *r, Var *s, Func *f){
    op = o;
    val = l;
    var = r;
    res = s;
    func = f;
    if (o == OP_CALL) {
        farg = l;
    }
}

Order::Order(Operation o, Func *f){
    op = o;
    func = f;
}

Order::Order(Operation o, Var *v){
    op = o;
    o == OP_DEF ? val = v : farg = v;
}

Order::Order(Operation o, Order *n, Var *v) {
    op = o;
    next = n;
    res = v;
}

Order::~Order() {
}

void OrderGenerator::genEntryCode(Func *func) {
    string funcName = "main" == func->getName() ? "_start" : genTempNo(1, func->getName());  //为当前函数生成一个唯一的标签名
    func->setName(funcName);
    Order *label = new Order(funcName);  //生成函数标签
    symtab.addOrder(label);  //添加函数标签指令
    symtab.addOrder(new Order(OP_ENTRY, func));  //添加函数入口指令
    func->setRet(label);  //设置函数返回标签，生成函数返回标签用
}

string OrderGenerator::genTempNo(int type, string prefix) {
    tempNo++;
    if (type == 0) {
        return prefix + "_" + to_string(tempNo);
    }
    return "L" + prefix + to_string(tempNo);
}

void OrderGenerator::genExitCode(Func *func) {
    if (func->getName() == "_start") {
        return;
    }
    symtab.addOrder(new Order(OP_EXIT, func));
}

void OrderGenerator::genOpCode(Operation op, Var *res, Var *val, Var *var) {
    if (op == OP_ADD || op == OP_SUB || op == OP_MUL || op == OP_DIV || op == OP_POINT) {
        symtab.addVar(res);
        symtab.getCurFunc()->allocEsp(res);
        genVarAssign(res);
        symtab.addOrder(new Order(op, val, var, res, nullptr));
    } else if (op == OP_ASSIGN) {
        symtab.addOrder(new Order(op, var, nullptr, val, nullptr));
    }
}

void OrderGenerator::genVarAssign(Var *var) {
    if (var->getName().rfind("^", 0) == 0) {
        return;
    }
    symtab.addOrder(new Order(OP_DEF, var));
    genOpCode(OP_ASSIGN, nullptr, var, var->getData());

}

OrderGenerator::OrderGenerator(Symtab &s, FILE *file):symtab(s),outfile(file) {
    symtab.initGen(this);
}

OrderGenerator::~OrderGenerator() {
    cout << "delete OrderGenerator" << endl;
}

void OrderGenerator::genMid() {
    fputs("-----------this is midcode-----------'\n", outfile);
    for (string name: symtab.getFuncSeq()) {
        Func *func = symtab.getFunc(name);
        vector<Order*> orders = func->getOrders();
        for (Order* order: orders){
            order->print(outfile);
        }
    }
}

void OrderGenerator::genRetCode(Var *var, Func *func) {
    if (!var) {
        return;
    }
    if(func->getName() == "_start"){
        symtab.addOrder(new Order(OP_QUIT, func));
        return;
    }
    Order *ret = func->getRet();
    symtab.addOrder(new Order(OP_RETURN, ret, var));
}

void OrderGenerator::genFuncCode(Operation op, Func *func, vector<Var *> args, Var *res) {
    if (op == OP_CALL) { //先将参数入栈
        if (!args.empty()) {
            for (int i = args.size() - 1; i >= 0; --i) {
                Var *v = args[i];
                if(func->getName().rfind("print") == 0){
                    continue;
                }
                symtab.addOrder(new Order(OP_PUSH_ARG, v));
            }
        }
        symtab.addOrder(new Order(OP_CALL,args[0], nullptr, res, func));
        symtab.addExtern(func->getName());
        if (res) {
            symtab.addVar(res);
            //计算变量的esp偏移
            symtab.getCurFunc()->allocEsp(res);
            genVarAssign(res);
        }
    }
}

Var* OrderGenerator::genLeftCode(Operation op, Var *v) {
    if(op ==OP_LEA || op == OP_GETV){
        Var *tmp = new Var(genTempNo(0, "temp"), nullptr, v->getType(), symtab.getScope(), op == OP_LEA);
        symtab.addVar(tmp);
        symtab.getCurFunc()->allocEsp(tmp);
        genVarAssign(tmp);
        symtab.addOrder(new Order(op, v, nullptr, tmp, symtab.getCurFunc()));
        return tmp;
    }else if(op == OP_SETV){
        Var *tmp = v->getData();
        symtab.addOrder(new Order(op, tmp, nullptr, v, symtab.getCurFunc()));
        return tmp;
    }
    return v;
}


void Order::print(FILE *midf) {
    if (label != "") {
        cout << label << endl;
        fputs(label.c_str(), midf);
        fputs("\n", midf);
        return;
    }
    if (op == OP_ADD || op == OP_SUB || op == OP_MUL || op == OP_DIV) {
        res->printMid(midf);
        fputs(opDesc[OP_ASSIGN], midf);
        val->printMid(midf);
        fputs(opDesc[op], midf);
        var->printMid(midf);
        fputs("\n", midf);
    } else if (op == OP_PUSH_ARG) {
        string push = "push arg ";
        fputs(push.c_str(), midf);
        farg->printMid(midf);
        fputs("\n", midf);
    } else if (op == OP_CALL) {
        string opDes = opDesc[op];
        opDes.append(" ").append(func->getName()).append("\n");
        fputs(opDes.c_str(), midf);
    } else if (op == OP_RETURN) {
        string opDes = opDesc[op];
        opDes.append(" ");
        fputs(opDes.c_str(), midf);
        res->printMid(midf);
        string nextStr = " ";
        nextStr.append(next->label);
        fputs(nextStr.c_str(), midf);
        fputs("\n", midf);
    } else if (op == OP_ENTRY) {
        string entry = "entry ";
        entry.append(func->getName());
        fputs(entry.c_str(), midf);
        fputs("\n", midf);
    } else if (op == OP_EXIT) {
        string exit = "exit ";
        exit.append(func->getName()).append("\n");
        fputs(exit.c_str(), midf);
    } else if (op == OP_DEF) {
        string def = "def ";
        fputs(def.c_str(), midf);
        val->printMid(midf);
        fputs("\n", midf);
    } else if (op == OP_ASSIGN) {
        if(!val){
            return;
        }
        res->printMid(midf);
        string ops = opDesc[op];
        fputs(ops.c_str(), midf);
        val->printMid(midf);
        fputs("\n", midf);
    } else if(op == OP_QUIT){
        fputs("\ncall quit", midf);
    } else if(op == OP_LEA){
        res->printMid(midf);
        fputs(" = &", midf);
        val->printMid(midf);
        fputs("\n", midf);
    }else if(op == OP_GETV){
        res->printMid(midf);
        fputs(" = *", midf);
        val->printMid(midf);
        fputs("\n", midf);
    }else if(op == OP_SETV){
        fputs(" *", midf);
        res->printMid(midf);
        fputs(" = ", midf);
        val->printMid(midf);
        fputs("\n", midf);
    } else if (op == OP_POINT) {
        res->printMid(midf);
        fputs(opDesc[OP_ASSIGN], midf);
        val->printMid(midf);
        fputs(opDesc[op], midf);
        var->printMid(midf);
        fputs("\n", midf);
    }
    else {
        cout << "not support other intermediate code: " << opDesc[op] << endl;
    }

}

void Order::genAsm(FILE *asmf) {
    if (label != "") {
        fprintf(asmf, "%s:\n", label.c_str());
        return;
    }
    if (op == OP_ENTRY) {
        fprintf(asmf, "\tpush ebp\n");
        fprintf(asmf, "\tmov ebp,esp\n");
        fprintf(asmf, "\tsub esp,%d\n", func->getMaxEsp());
    } else if (op == OP_EXIT) {
        fprintf(asmf, "\tleave\n");
        fprintf(asmf, "\tret\n");
    } else if (op == OP_RETURN) {
        if(!res){
            return;
        }
        if (res->getType() != NUM) {
            fprintf(asmf, "\tmov eax,[ebp%s%d]\n", MARK(res->getOffset()), res->getOffset());
        } else{
            fprintf(asmf, "\tmov DWORD eax,%d\n", res->getNumVal());
        }
    } else if (op == OP_ADD) {
        fprintf(asmf, "\tmov eax,[ebp%s%d]\n", MARK(val->getOffset()), val->getOffset());
        if(var->getType() == NUM ){
            fprintf(asmf, "\tadd eax,%d\n", var->getNumVal());
        } else{
            fprintf(asmf, "\tadd eax,[ebp%s%d]\n",MARK(var->getOffset()), var->getOffset());
        }
        fprintf(asmf, "\tmov [ebp%s%d],eax\n",MARK(res->getOffset()),res->getOffset());
    } else if (op == OP_SUB) {
        fprintf(asmf, "\tmov eax,[ebp%s%d]\n", MARK(val->getOffset()), val->getOffset());
        fprintf(asmf, "\tsub eax,[ebp%s%d]\n",MARK(var->getOffset()),var->getOffset());
        fprintf(asmf, "\tmov [ebp%s%d],eax\n",MARK(res->getOffset()),res->getOffset());
    } else if (op == OP_MUL) {
        fprintf(asmf, "\tmov eax,[ebp%s%d]\n", MARK(val->getOffset()), val->getOffset());
        fprintf(asmf, "\timul eax,[ebp%s%d]\n",MARK(var->getOffset()),var->getOffset());
        fprintf(asmf, "\tmov [ebp%s%d],eax\n",MARK(res->getOffset()),res->getOffset());
    } else if (op == OP_DIV) {
        fprintf(asmf, "\tmov eax,[ebp%s%d]\n", MARK(val->getOffset()), val->getOffset());
        fprintf(asmf, "\tidiv eax,[ebp%s%d]\n",MARK(var->getOffset()),var->getOffset());
        fprintf(asmf, "\tmov [ebp%s%d],eax\n",MARK(res->getOffset()),res->getOffset());
    } else if (op == OP_CALL) {
        if (func->isSys()) {
            string funcName = func->getName();
            if(funcName.rfind("print") == 0){
                bool isPrint = funcName == "print";
                string func;
                if(farg->getType() == NUM || farg->getType() == INT){
                    func = isPrint ? "\tcall iprint\n" : "\tcall iprintLF\n";
                    fprintf(asmf, "\tmov eax,[ebp%s%d]\n", MARK(farg->getOffset()), farg->getOffset());
                } else{
                    func = isPrint ? "\tcall sprint\n" : "\tcall sprintLF\n";
                    fprintf(asmf, "\tmov eax,%s\n", farg->getName().substr(1).c_str());
                }
                fprintf(asmf, func.c_str());
            } else if(funcName == "mem" || funcName == "mfr"){
                fprintf(asmf, "\tcall %s\n", funcName.c_str());
                fprintf(asmf, "\tmov [ebp%s%d],eax\n", MARK(res->getOffset()), res->getOffset());
            }
        } else{
            fprintf(asmf, "\tcall %s\n", func->getName().c_str());
            if (res) {
                fprintf(asmf, "\tmov [ebp%s%d],eax\n", MARK(res->getOffset()), res->getOffset());
            }
        }
    } else if (op == OP_PUSH_ARG) {
        if(farg->getType() == NUM){
            fprintf(asmf, "\tpush %d\n", farg->getNumVal());
        } else{
            fprintf(asmf, "\tpush DWORD [ebp%s%d]\n", MARK(farg->getOffset()), farg->getOffset());
        }
    } else if (op == OP_ASSIGN) {
        if (!val) {
            return;
        }
        if(val->isTemp()){//临时变量通过ebx转换
            fprintf(asmf, "\tmov ebx,[ebp%s%d]\n", MARK(val->getOffset()), val->getOffset());
            fprintf(asmf, "\tmov DWORD [ebp%s%d],ebx\n", MARK(res->getOffset()), res->getOffset(), val->getName().substr(1).c_str());
        } else{
            if (val->getType() == STR) {
                fprintf(asmf, "\tmov DWORD [ebp%s%d],%s\n", MARK(res->getOffset()), res->getOffset(), val->getName().substr(1).c_str());
            } else{
                fprintf(asmf, "\tmov DWORD [ebp%s%d],%d\n", MARK(res->getOffset()), res->getOffset(), val->getNumVal());
            }
        }
    } else if(op == OP_QUIT){
        fprintf(asmf, "\tcall quit\n");
    } else if(op == OP_LEA){
        fprintf(asmf, "\tlea ebx,DWORD [ebp%s%d]\n",MARK(val->getOffset()), val->getOffset());
        fprintf(asmf, "\tmov [ebp%s%d],ebx\n", MARK(res->getOffset()), res->getOffset());
    }else if(op == OP_GETV){
        Tag t = val->getData()->getType();
        fprintf(asmf, "\tmov ebx,DWORD [ebp%s%d]\n",MARK(val->getOffset()), val->getOffset());
        fprintf(asmf, "\tmov eax,[ebx]\n");
        fprintf(asmf, "\tmov [ebp%s%d],eax\n", MARK(res->getOffset()), res->getOffset());
    }else if(op == OP_SETV){
        if(val->getType() == NUM){
            fprintf(asmf, "\tmov edx,%d\n", val->getNumVal());
        } else{
            fprintf(asmf, "\tmov edx,DWORD [ebp%s%d]\n",MARK(val->getOffset()), val->getOffset());
        }
        fprintf(asmf, "\tmov eax,DWORD [ebp%s%d]\n",MARK(res->getOffset()), res->getOffset());
        fprintf(asmf, "\tmov DWORD [eax],edx\n");
    }else if (op == OP_POINT ) {
        fprintf(asmf, "\tmov ebx,[ebp%s%d]\n",MARK(var->getOffset()),var->getOffset());
        fprintf(asmf, "\tmov [ebp%s%d],ebx\n",MARK(res->getOffset()),res->getOffset());
    }

}



